<template>
  <div class="category">
    <el-row style="margin-bottom: 1rem">
      <el-button size="mini" @click="batchSave" type="primary">批量保存</el-button>
      <el-button size="mini" @click="batchDelete" type="danger">批量删除</el-button>
    </el-row>
    <el-tree :data="data"
             ref="tree"
             :default-expanded-keys="expandKeys"
             :expand-on-click-node="false"
             :node-key="'catId'"
             :props="defaultProps"
             :show-checkbox="true"
             :draggable="false"
             :allow-drag="allowDrag"
             :allow-drop="allowDrop"
             @node-drop="handleDrop"
             @node-click="handleNodeClick">
      <!--删除按钮-->
      <span slot-scope="{ node, data }" class="custom-tree-node">
        <span>{{ node.label }}</span>
        <span class="content">
          <el-button
            v-show="node.level !== 3"
            size="mini"
            type="text"
            @click="() => append(data)">
            添加
          </el-button>
          <el-button
            size="mini"
            type="text"
            @click="() => update(data)">
            修改
          </el-button>
          <el-button
            v-show="node.childNodes.length === 0"
            size="mini"
            type="text"
            @click="() => remove(node, data)">
            删除
          </el-button>
        </span>
      </span>
    </el-tree>
    <!--新增/修改对话框-->
    <el-dialog
      :title="title"
      :visible.sync="visible"
      :before-close="closeDialog"
      width="50%">
      <el-form :model="form">
        <el-form-item label="分类名称">
          <el-input v-model="form.name"/>
        </el-form-item>
        <el-form-item label="图标">
          <el-input v-model="form.icon"/>
        </el-form-item>
        <el-form-item label="计量单位">
          <el-input v-model="form.productUnit"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="closeDialog">取 消</el-button>
        <el-button v-if="form.catId === undefined" type="primary" @click="addCategory">确 定</el-button>
        <el-button v-else type="primary" @click="updateCategory">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "category",
  data() {
    return {
      data: [],
      maxLevel: 0,
      defaultProps: {
        children: 'children',
        label: 'name'
      },
      expandKeys: [],
      title: '',
      visible: false,
      form: {
        name: ''
      },
      sortedNodeList: []
    };
  },
  created() {
    this.getTreeList()
  },
  methods: {
    handleNodeClick(data) {
      console.log(data);
    },
    handleDrop(draggingNode, dropNode, dropType, ev) {
      // 寻找父节点逻辑
      // 知道当前节点最新父节点的ID
      let parentId = 0;
      // 排序逻辑
      // 该节点的所有兄弟节点
      // 排序这部分的循环判断放在父节点的判断中，因为判断你的逻辑重复了
      // 当前拖拽节点的最新顺序
      // 通过遍历节点进行排序，此时指代的遍历对象为这个节点的所有兄弟节点
      // 与他同一层级进行遍历排序即可完成该节点的排序
      let brothersNodeList = [];
      // 不同情况不同判断
      // 情况一：非进入某个节点，这种情况下父节点的ID = 兄弟节点父节点ID
      if (dropType === 'before' || dropType === 'after') {
        parentId = dropNode.parent.data.catId === undefined ? 0 : dropNode.parent.data.catId;
        // 这种情况下该节点的兄弟节点就是父节点的所有孩子节点
        // 这里使用VUE实例的childNodes
        brothersNodeList = dropNode.parent.childNodes
      } else if (dropType === 'inner') {
        // 情况二：进入某个节点，这种情况下的父节点的ID = 目标节点的ID
        parentId = dropNode.data.catId
        // 这种情况下该节点的兄弟节点就是进入节点的所有孩子节点
        // 这里使用VUE实例的childNodes
        brothersNodeList = dropNode.childNodes
      }

      // 调节当前节点的排序
      // 对修改节点顺序的逻辑需要兼顾所有的兄弟节点，因为一个节点动了，其所有的兄弟节点的位置
      // 也必然发生变化
      this.sortedNodeList = []
      for (let i = 0; i < brothersNodeList.length; i++) {
        let node = brothersNodeList[i]
        // 是否是当前拖拽接节点
        if (node.data.catId === draggingNode.data.catId) {
          // 拿到正在拖拽节点的level
          let level = draggingNode.level
          // 若层级是否发生变化(此处都用Vue的实例的level比较, 方便与数据库中的level做最后的数据同步)
          if (node.level !== level) {
            // 正在拖拽节点的层级 = 遍历节点的最新层级
            level = node.level
            // 更新其子节点的所有层级
            this.updateDeepNodeLevel(node)

            /*@Abandon*/
            // 是进入的方式还是前后的方式
            // 目标节点层级, 因为无论是前后方式也好还是进入方式也好, 目标节点都是dropNode表示
            // 前后的方式, 层级 = 兄弟节点的层级
            /*if (dropType === 'before' || dropType === 'after') {
              level = dropNode.level
            } else if (dropType === 'inner') {
              // 进入方式, 层级 = 目标节点的层级 + 1
              level = dropNode.level + 1
            }*/
          }
          // 那么当前拖拽节点的父ID需要修改
          this.sortedNodeList.push({
            catId: node.data.catId,
            parentCid: parentId,
            sort: i // 排序
          })
        } else {
          // 否则只需要修改节点的顺序就行了
          this.sortedNodeList.push({
            catId: node.data.catId,
            sort: i // 排序
          })
        }
      }

      // 当前拖拽节点的最新层级
      console.log(this.sortedNodeList)

    },
    /**
     * 更新节点其所有子节点的层级
     * 或者叫同步数据库层级变化, 因为此时入参的Node已经是最新的层级节点了相对于Vue实例
     *
     * @param node
     */
    updateDeepNodeLevel(node) {
      if (node.childNodes.length === 0) {
        return
      }
      for (let i = 0; i < node.childNodes.length; i++) {
        let currentNode = childNodes[i]
        // 节点最新层级
        let newLevel = currentNode.level
        // 传入批量更新列表中
        this.sortedNodeList.push({
          catId: currentNode.data.catId,
          catLevel: newLevel
        })
        // 递归更新子节点
        this.updateDeepNodeLevel(currentNode)
      }
    },
    allowDrag() {
      return true
    },
    allowDrop(draggingNode, dropNode, type) {
      this.maxLevel = 0;
      this.findDeepLength(draggingNode.data);
      // 深度：最大层级数 - 当前节点的层级数 + 1
      let deep = (this.maxLevel - draggingNode.data.catLevel) + 1;
      // 判断拖入还是拖出
      if (type === 'inner') {
        // 拖入进某个节点里面, 深度 + 目标节点的层级 <= 3
        return deep + dropNode.level <= 3
      }
      // 拖出某个节点, 深度 + 目标节点的父节点的层级 <=3
      return deep + dropNode.parent.level <= 3
    },
    /**
     * 找出节点的最大深度
     * v - v1 - v2  - v5 - v11
     *          |   - v6
     *          |   - v10
     *        - v3  - v7
     *          |   - v8
     *        - v4  - v9
     * 深度：5
     * @param node
     */
    findDeepLength(node) {
      // 有子节点才寻找
      if (node.children != null && node.children.length > 0) {
        // 循环其子节点寻找深度
        let children = node.children;
        for (let i = 0; i < children.length; i++) {
          let c = children[i]
          // 当前节点的等级若大于最大层级, 则更新本次循环的最大层级
          if (c.catLevel > this.maxLevel) {
            this.maxLevel = c.catLevel;
          }
          // 对这个遍历中的节点重复执行算法
          this.findDeepLength(c)
        }
      }
    },
    /**
     * 请求三级分类数据
     */
    getTreeList() {
      this.$http({
        url: this.$http.adornUrl(`/product/category/list/tree`),
        method: 'get'
      }).then(({data}) => {
        this.data = data.data;
      })
    },
    /**
     * 添加树节点
     * 接口请求
     */
    addCategory() {
      this.$http({
        url: this.$http.adornUrl(`/product/category/save`),
        method: 'post',
        data: this.$http.adornData(this.form, false)
      }).then(({data}) => {
        if (data && data.code === 0) {
          this.$message.success('操作成功')
          this.visible = false
          this.getTreeList()
          // 记住默认展开的节点
          this.expandKeys.push(this.form.parentCid)
          this.resetForm()
        } else {
          this.$message.error(data.msg)
        }
      })
    },
    /**
     * 修改树节点
     * 接口请求
     */
    updateCategory() {
      this.$http({
        url: this.$http.adornUrl(`/product/category/update`),
        method: 'post',
        data: this.$http.adornData(this.form, false)
      }).then(({data}) => {
        if (data && data.code === 0) {
          this.$message.success('操作成功')
          this.visible = false
          this.getTreeList()
          // 记住默认展开的节点
          this.expandKeys.push(this.form.catId)
          this.resetForm()
        } else {
          this.$message.error(data.msg)
        }
      })
    },
    batchSave() {

    },
    /**
     * 批量删除
     */
    batchDelete() {
      // 获取选中的树节点
      let delIndex = []
      this.$refs.tree.getCheckedNodes().forEach(node => {
        delIndex.push(node.catId)
      })
      this.$confirm(`确定对【${delIndex}】进行批量删除操作?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$http({
          url: this.$http.adornUrl(`/product/category/delete`),
          method: 'post',
          data: this.$http.adornData(delIndex, false)
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.$message.success('操作成功')
            this.getTreeList()
            // 记住默认展开的节点
            this.expandKeys.push(node.parent.data.catId)
          } else {
            this.$message.error(data.msg)
          }
        })
      }).catch(() => {
      })
    },
    closeDialog() {
      this.resetForm()
      this.visible = false
    },
    resetForm() {
      this.form = {}
    },
    /**
     * 添加树形节点
     * 事件
     * @param data
     */
    append(data) {
      this.form.parentCid = data.catId;
      this.form.catLevel = data.catLevel + 1;
      this.title = `为【${data.name}】添加子分类`;
      this.visible = true;
    },
    /**
     * 修改树形节点
     * 事件
     * @param data
     */
    update(data) {
      this.$http({
        url: this.$http.adornUrl(`/product/category/info/${data.catId}`),
        method: 'get'
      }).then(({data}) => {
        let category = data.data;
        this.$set(this.form, 'catId', category.catId);
        this.$set(this.form, 'name', category.name);
        this.$set(this.form, 'icon', category.icon);
        this.$set(this.form, 'productUnit', category.productUnit);
        this.title = `修改【${this.form.name}】分类`;
        this.visible = true;
      })
    },
    /**
     * 删除树形节点
     * @param node
     * @param data
     */
    remove(node, data) {
      // 删除的id数组
      let ids = [data.catId];
      this.$confirm(`确定对【${data.name}】进行删除操作?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$http({
          url: this.$http.adornUrl(`/product/category/delete`),
          method: 'post',
          data: this.$http.adornData(ids, false)
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.$message.success('操作成功')
            this.getTreeList()
            // 记住默认展开的节点
            this.expandKeys.push(node.parent.data.catId)
          } else {
            this.$message.error(data.msg)
          }
        })
      }).catch(() => {
      })
    },
  }
}
</script>

<style scoped>

</style>
